home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Multimedia Toolkit
/
Multimedia Toolkit.iso
/
pascal
/
s3mloade.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1993-03-26
|
19KB
|
608 lines
UNIT S3mLoader;
INTERFACE
USES Objects, SongUnit;
PROCEDURE LoadS2mFileFormat (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
PROCEDURE LoadS3mFileFormat (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
IMPLEMENTATION
USES SongElements, SongUtils, Heaps, AsciiZ;
{----------------------------------------------------------------------------}
{ Internal definitions. Format of the files. }
{____________________________________________________________________________}
TYPE
TS3mFileMagic1 = WORD;
TS3mFileMagic2 = ARRAY[0..3] OF CHAR;
TS2mFileMagic = ARRAY[0..3] OF CHAR;
CONST
S3mMagic1 = $101A;
S3mMagic2 : TS3mFileMagic2 = ( 'S', 'C', 'R', 'M' );
S3mInstr2 : TS3mFileMagic2 = ( 'S', 'C', 'R', 'S' );
S2mMagic : TS3mFileMagic2 = ( 'S', 'C', 'R', 'M' );
TYPE
TS3mHeader =
RECORD
Name : ARRAY[1..28] OF CHAR;
Magic1 : TS3mFileMagic1;
NPI1 : WORD;
SeqLen : WORD;
NInstruments: WORD;
NPatts : WORD;
Word4 : WORD;
Long1 : LONGINT;
Magic2 : TS3mFileMagic2;
Volume : BYTE;
Tempo : BYTE;
BPM : BYTE;
fill1 : ARRAY[1..13] OF BYTE;
ChannelMaps : ARRAY[1..32] OF BYTE;
END;
TS2mHeader =
RECORD
Name : ARRAY[1..20] OF CHAR;
Scream : ARRAY[1.. 8] OF CHAR;
Version : BYTE;
fill1 : ARRAY[1.. 3] OF BYTE;
PattOfs : WORD;
InstrOfs : WORD;
SeqOfs : WORD;
fill2 : ARRAY[1.. 4] OF BYTE;
Volume : BYTE;
Tempo : BYTE;
fill3 : ARRAY[1.. 4] OF BYTE;
NPatts : WORD;
NInstruments: WORD;
SeqLen : WORD;
Word4 : WORD;
Long1 : LONGINT;
Magic : TS2mFileMagic;
END;
TS3mInstrument =
RECORD
Flag : BYTE;
Name : ARRAY[1..13] OF CHAR;
Position : WORD;
Size : LONGINT;
RepStart : LONGINT;
RepLen : LONGINT;
Volume : WORD;
Byte1 : BYTE;
Looped : BOOLEAN;
PeriodFine: WORD;
fill3 : ARRAY[1..10] OF BYTE;
Word3 : WORD;
Word4 : WORD;
Comment : ARRAY[1..28] OF CHAR;
Id : TS3mFileMagic2;
END;
TOffsets = ARRAY[1..256] OF WORD;
TInstrFlags = ARRAY[1..256] OF BOOLEAN;
VAR
MaxChans : WORD;
InitialPos : LONGINT;
PROCEDURE SeekToOfs(VAR St: TStream; Ofs: WORD);
BEGIN
St.Seek(InitialPos + 16*LONGINT(Ofs));
END;
PROCEDURE ProcessPatterns(VAR Song: TSong; VAR St: TStream; VAR InstrFlags : TInstrFlags;
VAR PattOfs: TOffsets; Num: WORD; S3m: BOOLEAN; Vers: BYTE);
VAR
Patt : ARRAY[1..5000] OF BYTE;
FullTrack : TFullTrack;
Pattern : PPattern;
Track : PTrack;
Note : TFullNote;
c : BYTE;
i, j : WORD;
n, t : WORD;
Row : WORD;
Size : WORD;
NAdj : WORD;
l : LONGINT;
LastChan : WORD;
LABEL
Ya, No;
BEGIN
t := 1;
FOR n := 1 TO Num DO
BEGIN
FOR i := 1 TO Song.SequenceLength DO
IF Song.PatternSequence^[i] = n THEN GOTO Ya;
GOTO No;
Ya:
{WriteLn('Patt ', n : 3, FullHeap.HMaxAvail : 10, FullHeap.HMemAvail : 10);}
Pattern := Song.GetPattern(n);
IF Pattern = NIL THEN
BEGIN
Song.Status := msOutOfMemory;
EXIT;
END;
WITH Pattern^.Patt^ DO
BEGIN
NNotes := 64;
NChans := Song.NumChannels;
Tempo := 0;
BPM := 0;
END;
SeekToOfs(St, PattOfs[n]);
IF S3m OR (Vers > $0E) THEN
St.Read(Size, 2)
ELSE
Size := SizeOf(Patt) + 2;
DEC(Size, 2);
IF Size > SizeOf(Patt) THEN
Size := SizeOf(Patt);
St.Read(Patt, Size);
IF St.Status <> stOk THEN
BEGIN
Song.Status := msFileTooShort;
EXIT;
END;
LastChan := 1;
FOR j := 1 TO Song.NumChannels DO
BEGIN
FillChar(FullTrack, SizeOf(FullTrack), 0);
i := 1;
Row := 0;
WHILE (i <= Size) AND
(S3m OR (Row < 64)) DO
BEGIN
c := Patt[i];
INC(i);
IF c = 0 THEN
Inc(Row)
ELSE IF (c AND $1F) = (j - 1) THEN
BEGIN
FillChar(Note, SizeOf(Note), 0);
IF c AND $20 <> 0 THEN
BEGIN
Note.Period := Patt[i];
IF NOT S3m THEN
INC(Note.Period, $20);
IF ((Note.Period AND $F0) > $90) OR
((Note.Period AND $F0) < $20) OR
((Note.Period AND $0F) > $0B) THEN
Note.Period := 0;
IF Note.Period <> 0 THEN
BEGIN
Note.Period := PeriodSet[
(Note.Period SHR 4) - 2, Note.Period AND 15];
IF MaxChans <= (c AND $1F) THEN
MaxChans := (c AND $1F) + 1;
END;
Note.Instrument := Patt[i+1];
IF Note.Instrument <> 0 THEN
InstrFlags[Note.Instrument] := TRUE;
INC(i, 2);
END;
IF c AND $40 <> 0 THEN
BEGIN
Note.Volume := Patt[i] + 1;
IF Note.Volume > 64 THEN
Note.Volume := 64;
INC(i, 1);
END;
IF c AND $80 <> 0 THEN
BEGIN
Note.Parameter := Patt[i+1];
CASE Patt[i] OF
1 : BEGIN
Note.Command := mcSetTempo;
IF NOT S3m THEN
Note.Parameter := Note.Parameter SHR 4;
END;
2 : BEGIN
Note.Command := mcJumpPattern;
INC(Note.Parameter);
END;
3 : Note.Command := mcEndPattern;
4 : BEGIN
IF Note.Parameter > $F0 THEN
BEGIN
Note.Command := mcVolFineDown;
Note.Parameter := Note.Parameter AND $F;
END
ELSE IF ((Note.Parameter AND $F) = $F) AND
(Note.Parameter > $F) THEN
BEGIN
Note.Command := mcVolFineUp;
Note.Parameter := Note.Parameter SHR 4;
END
ELSE
Note.Command := mcVolSlide;
END;
5 : BEGIN
IF Note.Parameter > $F0 THEN
BEGIN
Note.Command := mcFinePortaDn;
Note.Parameter := Note.Parameter AND $F;
END
ELSE
Note.Command := mcTPortDown;
END;
6 : BEGIN
IF Note.Parameter > $F0 THEN
BEGIN
Note.Command := mcFinePortaUp;
Note.Parameter := Note.Parameter AND $F;
END
ELSE
Note.Command := mcTPortUp;
END;
7 : Note.Command := mcNPortamento;
8 : Note.Command := mcVibrato;
10 : Note.Command := mcArpeggio;
ELSE
Note.Command := TModCommand(ORD(mcLast) + Patt[i]);
END;
IF ((Note.Command = mcEndPattern) OR (Note.Command = mcJumpPattern)) AND
(Pattern^.Patt^.NNotes > Row + 1) THEN
Pattern^.Patt^.NNotes := Row + 1;
INC(i, 2);
END;
FullTrack[Row] := Note;
END
ELSE
BEGIN
IF (j = 1) AND (LastChan < (c AND $1F) + 1) THEN
LastChan := (c AND $1F) + 1;
IF c AND $20 <> 0 THEN INC(i, 2);
IF c AND $40 <> 0 THEN INC(i, 1);
IF c AND $80 <> 0 THEN INC(i, 2);
END;
END;
Track := Song.GetTrack(t);
IF Track = NIL THEN
BEGIN
Song.Status := msOutOfMemory;
EXIT;
END;
Track^.SetFullTrack(FullTrack);
Pattern^.Patt^.Channels[j] := t;
INC(t);
IF j > LastChan THEN GOTO No;
END;
No:
END;
END;
PROCEDURE ProcessInstruments(VAR Song: TSong; VAR St: TStream; VAR InstrFlags : TInstrFlags;
VAR InstrOfs: TOffsets; Num: WORD; S3m: BOOLEAN; Vers: BYTE);
VAR
Instrument : TInstrumentRec;
Instr : PInstrument;
S3mInstr : TS3mInstrument;
i, w : WORD;
Signo : LONGINT;
NoSigno : LONGINT;
BEGIN
FOR i := 1 TO Num DO
WITH Instrument DO
BEGIN
{WriteLn('Instr ', i : 3, FullHeap.HMaxAvail : 10, FullHeap.HMemAvail : 10);}
FillChar(Instrument, SizeOf(Instrument), 0);
Instr := Song.GetInstrument(i);
IF Instr = NIL THEN
BEGIN
Song.Status := msOutOfMemory;
EXIT;
END;
SeekToOfs(St, InstrOfs[i]);
St.Read(S3mInstr, SizeOf(S3mInstr));
IF S3mInstr.Flag = 1 THEN
BEGIN
Instr^.SetName(StrASCIIZ(S3mInstr.Comment, 22));
IF InstrFlags[i] THEN
Len := S3mInstr.Size;
IF Len > 0 THEN
BEGIN
IF S3mInstr.Looped THEN
BEGIN
Reps := S3mInstr.RepStart;
Repl := S3mInstr.RepLen;
END
ELSE
BEGIN
Reps := 0;
Repl := 0;
END;
Vol := S3mInstr.Volume;
DAdj := S3mInstr.PeriodFine;
IF S3m THEN
NAdj := $20AB
ELSE
NAdj := $2100;
IF Repl > Len THEN Repl := Len;
IF Reps + Repl > Len THEN Repl := Len - Reps;
IF Vol > $40 THEN
Vol := $40;
SeekToOfs(St, S3mInstr.Position);
IF Len <= MaxSample THEN
BEGIN
FullHeap.HGetMem(POINTER(Data), Len);
IF Data = NIL THEN BEGIN
Song.Status := msOutOfMemory;
EXIT;
END;
St.Read(Data^, Len);
IF St.Status <> stOk THEN BEGIN
Song.Status := msFileDamaged;
EXIT;
END;
Signo := 0;
NoSigno := 0;
FOR w := 1 TO Len - 1 DO
BEGIN
IF (Data^[w-1] XOR Data^[w]) AND $80 <> 0 THEN
BEGIN
IF (SHORTINT(Data^[w] - 64) < 0) AND
(SHORTINT(Data^[w-1] - 64) < 0) THEN
INC(Signo)
ELSE IF (SHORTINT(Data^[w] - 64) >= 0) AND
(SHORTINT(Data^[w-1] - 64) >= 0) THEN
INC(NoSigno)
END;
END;
IF NoSigno > Signo THEN
FOR w := 0 TO Len - 1 DO
INC(Data^[w], 128);
END
ELSE
BEGIN
FullHeap.HGetMem(POINTER(Data), MaxSample);
FullHeap.HGetMem(POINTER(Xtra), Len-MaxSample);
IF (Data = NIL) OR (Xtra = NIL) THEN BEGIN
Song.Status := msOutOfMemory;
EXIT;
END;
St.Read(Data^, MaxSample);
St.Read(Xtra^, Len-MaxSample);
IF St.Status <> 0 THEN BEGIN
Song.Status := msFileDamaged;
EXIT;
END;
END;
Instr^.Change(@Instrument);
END
ELSE
Instr^.Change(NIL);
END;
END;
END;
PROCEDURE LoadS3mFileFormat(VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
VAR
Hdr : TS3mHeader ABSOLUTE Header;
InstrOfs : TOffsets;
PattOfs : TOffsets;
i : WORD;
InstrFlags : TInstrFlags;
BEGIN
Song.FileFormat := mffS3m;
InitialPos := St.GetPos;
St.Seek(InitialPos + SizeOf(TS3mHeader));
IF {(Hdr.Magic1 <> S3mMagic1) OR }(Hdr.Magic2 <> S3mMagic2) THEN
BEGIN
Song.Status := msNotLoaded;
EXIT;
END;
Song.Status := msOK;
FillChar(InstrFlags, SizeOf(InstrFlags), 0);
Song.Name := FullHeap.HNewStr(StrASCIIZ(Hdr.Name, 28));
IF Hdr.Volume = 64 THEN Hdr.Volume := 63;
Song.FirstTick := TRUE;
Song.InitialTempo := Hdr.Tempo;
Song.InitialBPM := Hdr.BPM;
Song.Volume := Hdr.Volume * 4 + 3;
Song.NumChannels := MaxChannels;
MaxChans := 1;
Song.SequenceRepStart := 0;{Hdr.NPI1 + 1;}
St.Read(Song.PatternSequence^, Hdr.SeqLen);
IF Hdr.SeqLen > Song.SongLen THEN
Hdr.SeqLen := Song.SongLen;
Song.SequenceLength := Hdr.SeqLen;
FOR i := 1 TO Hdr.SeqLen DO
INC(Song.PatternSequence^[i]);
St.Read(InstrOfs, Hdr.NInstruments*2);
St.Read(PattOfs, Hdr.NPatts*2);
WHILE (Song.SequenceLength > 1) AND
(Song.PatternSequence^[Song.SequenceLength] = 0) DO
DEC(Song.SequenceLength);
FOR i := 1 TO Song.SongStart - 1 DO
Song.PatternSequence^[i] := 0;
{ Processing of the patterns (the partiture) }
ProcessPatterns(Song, St, InstrFlags, PattOfs, Hdr.NPatts, TRUE, $FF);
IF Song.Status > msOk THEN EXIT;
{ Processing of the instruments }
ProcessInstruments(Song, St, InstrFlags, InstrOfs, Hdr.NInstruments, TRUE, $FF);
IF Song.Status > msFileTooShort THEN EXIT;
IF Song.NumChannels > MaxChans THEN
Song.NumChannels := MaxChans;
END;
PROCEDURE LoadS2mFileFormat(VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
VAR
Hdr : TS2mHeader ABSOLUTE Header;
InstrOfs : TOffsets;
PattOfs : TOffsets;
i : WORD;
InstrFlags : TInstrFlags;
BEGIN
Song.FileFormat := mffS2m;
InitialPos := St.GetPos;
St.Seek(InitialPos + SizeOf(TS2mHeader));
IF Hdr.Magic <> S2mMagic THEN
BEGIN
Song.Status := msNotLoaded;
EXIT;
END;
Song.Status := msOK;
FillChar(InstrFlags, SizeOf(InstrFlags), 0);
Song.Name := FullHeap.HNewStr(StrASCIIZ(Hdr.Name, 20));
IF Hdr.Volume = 64 THEN Hdr.Volume := 63;
Song.FirstTick := TRUE;
Song.InitialTempo := Hdr.Tempo SHR 4;
Song.InitialBPM := 125;
Song.Volume := Hdr.Volume * 4 + 3;
Song.NumChannels := MaxChannels;
MaxChans := 1;
Song.SequenceRepStart := 0;
SeekToOfs(St, Hdr.InstrOfs);
St.Read(InstrOfs, (Hdr.NInstruments*2 + 15) AND $FFF0);
SeekToOfs(St, Hdr.PattOfs);
St.Read(PattOfs, (Hdr.NPatts*2 + 15) AND $FFF0);
SeekToOfs(St, Hdr.SeqOfs);
St.Read(Song.PatternSequence^, 16);
St.Read(Song.PatternSequence^, 16);
DEC(Hdr.SeqLen);
FOR i := 1 TO Hdr.SeqLen DO
BEGIN
St.Read(Song.PatternSequence^[i], 5);
INC(Song.PatternSequence^[i]);
END;
IF Hdr.SeqLen > Song.SongLen THEN
Hdr.SeqLen := Song.SongLen;
Song.SequenceLength := Hdr.SeqLen;
WHILE (Song.SequenceLength > 1) AND
(Song.PatternSequence^[Song.SequenceLength] = 0) DO
DEC(Song.SequenceLength);
FOR i := 1 TO Song.SongStart - 1 DO
Song.PatternSequence^[i] := 0;
{ Processing of the patterns (the partiture) }
ProcessPatterns(Song, St, InstrFlags, PattOfs, Hdr.NPatts, FALSE, Hdr.Version);
IF Song.Status > msOk THEN EXIT;
{ Processing of the instruments }
ProcessInstruments(Song, St, InstrFlags, InstrOfs, Hdr.NInstruments, FALSE, Hdr.Version);
IF Song.Status > msFileTooShort THEN EXIT;
IF Song.NumChannels > MaxChans THEN
Song.NumChannels := MaxChans;
END;
END.